﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using ProgNetComponents.ExtenderProviders.Settings;
using System.Collections;

namespace ProgNetComponents.ExtenderProviders
{
    public abstract class BaseExtenderProvider : Components.CustomTypeDescriptorComponent
    {
        protected IContainer container;
        public abstract IEnumerable<BaseExtenderSettings> GetAllSettings();
        public abstract IEnumerable<object> GetAllKeys();
        public BaseExtenderProvider(IContainer container) : base()
        {
            this.container = container;
            if (container != null)
            {
                container.Add(this);
            }
        }
        public BaseExtenderProvider() { }

    }

    [ProvideProperty("Settings", typeof(object)),
    DesignTimeVisibleAttribute(true)]
    public abstract class BaseExtenderProvider<T> :BaseExtenderProvider, IExtenderProvider, ISupportInitialize where T: Settings.BaseExtenderSettings
    {
        public BaseExtenderProvider(IContainer container): base(container)
        {

        }
        public BaseExtenderProvider()
        {

        }
        protected bool _IsInitializing;
        protected List<object> _ChangedKeys = new List<object>();
        protected Dictionary<object, T> _AllSettings = new Dictionary<object, T>();
        public override IEnumerable<object> GetAllKeys()
        {
            return _AllSettings.Keys;
        }
        public override IEnumerable<BaseExtenderSettings> GetAllSettings()
        {
            return _AllSettings.Values;
        }
        [
            DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
            RefreshProperties(RefreshProperties.All),
            Category(Categories.ProgNetSettings)
        ]
        public T GetSettings(object owner)
        {
            object key = GetRealKey(owner);
            if (key == null)
                return null;

            if (_AllSettings.ContainsKey(key))
                return _AllSettings[key];

            if (CanExtend(key))
            {
                T s = CreateSettings(key);
                s.SettingsChanged += new EventHandler(SettingsChanged);
                _AllSettings.Add(key, s);
                SettingsCreated(owner, s);
                return _AllSettings[key];
            }
            return null;
        }
        [
            DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
            RefreshProperties(RefreshProperties.All),
            Category(Categories.ProgNetSettings)
        ]
        public void SetSettings(object owner, T settings)
        {
            //throw new NotImplementedException("SetSettings");
        }

        protected virtual void SettingsCreated(object sender, T settings)
        {

        }

        protected abstract void ProcessObject(object sender, T settings);

        protected void RemoveKeyFromChanged(object sender)
        {
            if (sender != null)
            {
                object key = GetRealKey(sender);
                if (key != null && _ChangedKeys.Contains(key))
                    _ChangedKeys.Remove(key);
            }
        }

        protected void AddKeyToChanged(object sender)
        {
            if (sender != null)
            {
                object key = GetRealKey(sender);
                if (key != null && !_ChangedKeys.Contains(key))
                    _ChangedKeys.Add(key);
            }
        }
        
        protected void DoProcessObject(object sender)
        {
            T settings  = GetSettings(sender);
            if (settings != null)
            {
                ProcessObject(sender, settings);
                RemoveKeyFromChanged(sender);
            }
        }

        protected void SettingsChanged(object sender, EventArgs e)
        {
            if (_IsInitializing)
            {
                AddKeyToChanged(sender);
            }
            else
            {
                DoProcessObject(sender);
            }
        }

        protected virtual T CreateSettings(object owner)
        {
            return (T)Activator.CreateInstance(typeof(T), owner, this);
        }

        protected virtual object GetRealKey(object key)
        {
            return key;
        }
        protected virtual void ProcessObject(object sender)
        {
            T s = GetSettings(sender);
            if (s != null)
                ProcessObject(sender, s);
        }
        protected void ProcessAllChanged()
        {
            object[] tab = _ChangedKeys.ToArray();
            foreach (object o in tab)
                DoProcessObject(o);
        }
        protected void ProcessAll()
        {
            object[] tab = _AllSettings.Keys.ToArray();
            foreach (object o in tab)
                DoProcessObject(o);
        }

        public void RefreshObject(object sender)
        {
            ProcessObject(sender);
        }
        public void RefreshObjects(params object[] senders)
        {
            if (senders != null)
            {
                foreach (object o in senders)
                    ProcessObject(o);
            }
        }
        public void RefreshAllObjects()
        {
            ProcessAll();
        }

        #region ISupportInitialize Members

        public virtual void BeginInit()
        {
            _IsInitializing = true;
        }

        public virtual void EndInit()
        {
           _IsInitializing = false;
           ProcessAllChanged();
        }

        #endregion

        #region IExtenderProvider Members

        public virtual bool CanExtend(object extendee)
        {
            return true;
        }

        #endregion
    }
}
